/*
  S.M.A.C.K - An operating system kernel
  Copyright (C) 2010,2011 Mattias Holm and Kristian Rietveld
  For licensing and a full list of authors of the kernel, see the files
  COPYING and AUTHORS.
*/

#include <sync.h>
#include <process.h>
void
spin_init(spinlock_t *spinlock)
{
  spinlock->flag = 0;
}

void
spin_lock(spinlock_t *spinlock)
{
  // Spin until the sync lock succeeds, i.e. returns previous lock value of 0
  while (hw_sync_lock_acquire(&spinlock->flag, 1)) ;
}

void
spin_unlock(spinlock_t *spinlock)
{
  hw_sync_lock_release(&spinlock->flag);
}

void
mutex_init(mutex_t *mutex)
{
  spin_init(&mutex->lock);
  mutex->proc = NULL;
  mutex->level = 0;
}

void
mutex_lock(mutex_t *mutex)
{
  spin_lock(&mutex->lock);

  if (mutex->level == 0) {
    mutex->proc = proc_current();
    mutex->level ++;
  } else if (mutex->proc == proc_current()) {
    mutex->level ++;
  } else {
    proc_t *proc = proc_current();

    proc_block(&mutex->block_list, proc);

    spin_unlock(&mutex->lock);
  }

  spin_unlock(&mutex->lock);
}


bool
mutex_try_lock(mutex_t *mutex)
{
  bool result = false;
  spin_lock(&mutex->lock);

  if (mutex->level == 0) {
    mutex->proc = proc_current();
    mutex->level ++;
    result = true;
  } else if (mutex->proc == proc_current()) {
    mutex->level ++;
    result = true;
  } else {
    result = false;
  }
  spin_unlock(&mutex->lock);

  return result;
}

void
mutex_unlock(mutex_t *mutex)
{
  spin_lock(&mutex->lock);
  mutex->level --;
  if (mutex->level == 0) mutex->proc = NULL;

  proc_unblock_list(&mutex->block_list);

  spin_unlock(&mutex->lock);
}
